import type { Ref } from 'vue'
import { reactive } from 'vue'
import type {
  BaseNode,
  BaseNodeType,
  EventNode,
  ExpressionNode,
  GatewayNode,
  ServiceNode,
  SubprocessNode,
  TaskNode,
} from '@/types'
import { ids } from '@/configuration/uuid'

// 节点记录表
export const nodeMaps: Map<BaseNode['id'], BaseNode> = new Map()
/**
 * 记录新节点
 * @param node
 */
export function setNodeInMap(node: BaseNode) {
  nodeMaps.set(node.id, node)
  return node
}
/**
 * 读取节点
 * @param id 节点 id
 */
export function getNodeInMap(id: BaseNode['id']) {
  return nodeMaps.get(id)
}
/**
 * 移除指定节点
 * @param id
 */
export function removeNodeInMap(id: BaseNode['id']) {
  const node = nodeMaps.get(id)
  nodeMaps.delete(id)
  return node
}
/**
 * 清空节点记录表
 */
export function clearNodeMap() {
  nodeMaps.clear()
}

export const DEFAULT_NAME_MAP = {
  event: '事件',
  gateway: '网关',
  task: '任务',
  service: '服务',
  subprocess: '子流程',
  expression: '条件',
}
export const DEFAULT_BPMN_TYPE_MAP = {
  event: 'event',
  gateway: 'exclusiveGateway',
  task: 'task',
  service: 'serviceTask',
  subprocess: 'subProcess',
  expression: 'sequenceFlow',
}

/**
 * 创建节点
 */
export function createNode(
  type: 'gateway',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): GatewayNode
export function createNode(
  type: 'expression',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): ExpressionNode
export function createNode(
  type: 'task',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): TaskNode
export function createNode(
  type: 'service',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): ServiceNode
export function createNode(
  type: 'event',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): EventNode
export function createNode(
  type: 'subprocess',
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): SubprocessNode
export function createNode<T extends BaseNodeType>(
  type: T,
  parent?: BaseNode,
  name?: string,
  bo?: Record<string, unknown>,
): BaseNode {
  const n = name || DEFAULT_NAME_MAP[type] || type
  const id = ids(type)
  const base: BaseNode = reactive({
    id,
    type,
    name: n,
    $prev: undefined,
    $next: undefined,
    $parent: parent,
    businessData: { $type: DEFAULT_BPMN_TYPE_MAP[type] || type, ...(bo || {}), id },
  })

  if (type === 'gateway') {
    const $expressions: ExpressionNode[] = []
    const gatewayNode = reactive({
      ...base,
      $expressions,
    }) as GatewayNode

    const prefix = bo?.$type === 'parallelGateway' ? '分支' : '条件'
    const expressionNode1: ExpressionNode = createNode('expression', gatewayNode, `${prefix}-1`)
    const expressionNode2: ExpressionNode = createNode('expression', gatewayNode, `${prefix}-2`)

    $expressions.push(expressionNode1, expressionNode2)

    if (bo?.$type !== 'parallelGateway') {
      gatewayNode.$default = expressionNode1
    }

    setNodeInMap(expressionNode1)
    setNodeInMap(expressionNode2)

    return gatewayNode
  }
  if (type === 'expression') {
    return reactive({ ...base }) as ExpressionNode
  }
  if (type === 'service') {
    return reactive({ ...base }) as ServiceNode
  }
  if (type === 'task') {
    return reactive({ ...base }) as TaskNode
  }
  if (type === 'subprocess') {
    const subprocess = reactive({ ...base }) as SubprocessNode

    const subprocessStart = createNode('event', subprocess, '开始', {
      $type: 'startEvent',
      cls: 'start-event',
    })
    const subprocessEnd = createNode('event', subprocess, '结束', { $type: 'endEvent', cls: 'end-event' })
    subprocessStart.$next = subprocessEnd
    subprocessEnd.$prev = subprocessStart
    subprocess.$start = subprocessStart
    setNodeInMap(subprocessStart)
    setNodeInMap(subprocessEnd)

    return subprocess
  }
  if (type === 'event') {
    return reactive({ ...base }) as EventNode
  }

  return reactive({ ...base })
}

/**
 * 追加新节点
 * @param curNode 当前节点
 * @param newNode 追加节点
 */
export function appendNode(
  curNode: Ref<BaseNode>,
  newNode: Ref<BaseNode>,
): Ref<BaseNode> {
  const nextNode = curNode.value.$next

  curNode.value.$next = newNode.value
  newNode.value.$prev = curNode.value

  // 更新 parent
  newNode.value.$parent = curNode.value.$parent

  if (nextNode) {
    nextNode.$prev = newNode.value
    newNode.value.$next = nextNode
  }

  setNodeInMap(newNode.value)

  return newNode
}

/**
 * 移除节点
 * @param curNode 被移除的目标节点
 */
export function removeNode(curNode: BaseNode): BaseNode {
  const $prev = curNode.$prev
  const $next = curNode.$next
  if ($prev) {
    $prev.$next = $next
  }
  if ($next) {
    $next.$prev = $prev
  }

  curNode.$prev = undefined
  curNode.$next = undefined

  removeNodeInMap(curNode.id)

  if (curNode.type === 'gateway') {
    removeGatewayNode(curNode as GatewayNode)
  }
  if (curNode.type === 'expression') {
    removeExpressionNode(curNode as ExpressionNode)
  }
  if (curNode.type === 'subprocess') {
    removeSubprocessNode(curNode as SubprocessNode)
  }

  return curNode
}
// 移除条件节点
export function removeExpressionNode(node: ExpressionNode) {
  // step 1: 从父节点中移除
  const parent = node.$parent
  parent.$expressions = parent.$expressions.filter(e => e.id !== node.id)
  // step 2: 移除后续节点
  let nextNode: BaseNode | undefined = node.$next
  while (nextNode) {
    const n = removeNode(nextNode)
    nextNode = n.$next
  }
  // step 3: 如果移除该条件后剩余条件不足 2，则同时移除网关节点
  if (node.$parent.$expressions.length < 2) {
    removeNode(node.$parent)
  }
}
// 移除子流程节点
export function removeSubprocessNode(node: SubprocessNode) {
  // step 1: 移除内部节点
  let nextNode: BaseNode | undefined = node.$start
  while (nextNode) {
    const n = removeNode(nextNode)
    nextNode = n.$next
  }
}
// 移除网关节点
export function removeGatewayNode(node: GatewayNode) {
  for (const nodeElement of node.$expressions) {
    removeNode(nodeElement)
  }
}

/**
 * 移动节点到目标节点后面
 * @param targetNode 目标节点
 * @param node 被移动节点
 */
export function moveNode(
  targetNode: Ref<BaseNode>,
  node: Ref<BaseNode>,
): Ref<BaseNode> {
  setDragData()
  removeNode(node.value)
  return appendNode(targetNode, node)
}

/**
 * 设置节点拖动状态
 */
let dragData: Ref<BaseNode> | undefined
/**
 * 设置被拖拽节点
 // * @param event 事件对象
 * @param node 被拖拽节点
 */
export function setDragData(node?: Ref<BaseNode>) {
  dragData = node
  // event.dataTransfer?.setData('node-id', node.value.id)
}
/**
 * 获取被拖拽节点
 // * @param event 事件对象
 */
export function getDragData() {
  // event.preventDefault()
  // const id = event.dataTransfer?.getData('node-id')
  //
  // if (!id) {
  //   return
  // }
  // return getNodeInMap(id)
  return dragData
}

/**
 * 创建基础节点数据
 */
export function createPresetProcess() {
  const start = createNode('event', undefined, '开始', {
    $type: 'startEvent',
    cls: 'start-event',
    name: '开始',
  })
  const end = createNode('event', undefined, '结束', {
    $type: 'endEvent',
    cls: 'end-event',
    name: '结束',
  })

  setNodeInMap(start)
  setNodeInMap(end)

  start.$next = end
  end.$prev = start

  return start
}

/**
 * 节点类型判断部分
 */
export function isEventNode(node: BaseNode): node is EventNode {
  return node.type === 'event'
}
export function isTaskNode(node: BaseNode): node is TaskNode {
  return node.type === 'task'
}
export function isGatewayNode(node: BaseNode): node is GatewayNode {
  return node.type === 'gateway'
}
export function isExpressionNode(node: BaseNode): node is ExpressionNode {
  return node.type === 'expression'
}
export function isServiceNode(node: BaseNode): node is ServiceNode {
  return node.type === 'service'
}
export function isSubprocessNode(node: BaseNode): node is SubprocessNode {
  return node.type === 'subprocess'
}
